package com.chinacoal.microservice.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.chinacoal.microservice.mapper.CcmsSmartleasingTenantMapper;
import com.chinacoal.microservice.pojo.BasicConfiguration;
import com.chinacoal.microservice.service.WechatService;
import com.chinacoal.microservice.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author zhuliang
 * @version V1.0
 * @Description: 微信登录-后端实现类
 * @date 2020-12-21 10：05
 */
@Service
@Slf4j
public class WechatServiceImpl implements WechatService {

    private final static Charset UTF8 = StandardCharsets.UTF_8;
    private final static String CT_JSON = "application/json; charset=utf-8";

    @Autowired
    private CcmsSmartleasingTenantMapper tenantMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private BasicConfiguration basicConfiguration;

    /**
     * @Description: 用户名，密码登录
     * @author zhuliang
     * @date 2020/12/21 18:17
     */
    @Override
    public Result<String> usernamePasswordLogin(String userName, String passWord) {
        //查询是否有这个租户
        String login = tenantMapper.judgmentLogin(userName, passWord);

        //判断是否登录成功
        if (StringUtils.isBlank(login)) {
            log.error(CodeMsg.INCORRECT_USERNAME_OR_PASSWORD.toString() + "-->账号：" + userName + "-->密码：" + passWord);
            return Result.error(CodeMsg.INCORRECT_USERNAME_OR_PASSWORD);
        }

        //生成token
        String token = OrderNumberUtil.sign(userName);

        //存入到rdies中
        redisTemplate.opsForValue().set("wechat:" + userName, token, 1, TimeUnit.HOURS);

        return Result.success(token);
    }

    /**
     * @Description: 发送验证码
     * @author zhuliang
     * @date 2020/12/22 9:42
     */
    @Override
    public Result verificationCode(String phoneNumber, Integer type) {
        //验证电话号码
        if (!IdcardUtils.isPhoneLegal(phoneNumber)) {
            log.error(CodeMsg.PHONE_ABNORMAL.toString() + "-->电话号码:" + phoneNumber);
            return Result.error(CodeMsg.PHONE_ABNORMAL);
        }


        try {
            //设置6位随机数，为验证码
            int code = (int) ((Math.random() * 9 + 1) * 100000);

            String requstSms = sendVerificationCode(code + "", phoneNumber);

            log.info("发送给" + phoneNumber + "的验证码为：" + code);

            //判断是修改密码的验证码，还是登录的验证码,将其写入到redis中
            if (0 == type) {
                redisTemplate.opsForValue().set("wechatVerificationCode:" + phoneNumber, code, 2, TimeUnit.MINUTES);
            }else if (1 == type) {
                redisTemplate.opsForValue().set("updateVerificationCode:" + phoneNumber, code, 2, TimeUnit.MINUTES);
            }

            //验证是否成功
            HashMap map = JSONObject.parseObject(requstSms, HashMap.class);
            HashMap response = JSONObject.parseObject(map.get("Response") + "", HashMap.class);
            List<HashMap> sendStatusSet = JSONObject.parseArray(response.get("SendStatusSet") + "", HashMap.class);
            String codeSms = sendStatusSet.get(0).get("Code") + "";

            if ("Ok".equalsIgnoreCase(codeSms)){
                return Result.success();
            } else {
                return Result.error(CodeMsg.GET_VERIFICATION_CODE_FAILED);
            }

        } catch (Exception e) {
            log.error(CodeMsg.GET_VERIFICATION_CODE_FAILED.toString() + "-->" + e);
            return Result.error(CodeMsg.GET_VERIFICATION_CODE_FAILED);
        }
    }

    /**
     * @Description: 验证码登录
     * @author zhuliang
     * @date 2020/12/22 13:55
     */
    @Override
    public Result<String> verificationCodeLogin(String phoneNumber, int verificationCode) {
        //根据电话号码，从redis中获取验证码
        Object code = redisTemplate.opsForValue().get("wechatVerificationCode:" + phoneNumber);

        //判断验证码是否正确
        if (null == code || "null".equals(code) || "".equals(code) || !code.equals(verificationCode)) {
            log.error(CodeMsg.VERIFICATION_CODE_ERROR.toString() + "-->电话：" + phoneNumber + "-->用户验证码：" + verificationCode + "-->验证码：" + code);
            return Result.error(CodeMsg.VERIFICATION_CODE_ERROR);
        }

        //判断电话号码是否是合法租户
        String login = tenantMapper.judgmentLogin(phoneNumber, null);

        //判断是否登录成功
        if (StringUtils.isBlank(login)) {
            log.error(CodeMsg.NO_PRIVILEGES.toString() + "-->账号：" + phoneNumber);
            return Result.error(CodeMsg.NO_PRIVILEGES);
        }

        //生成token
        String token = OrderNumberUtil.sign(phoneNumber);

        //存入到rdies中
        redisTemplate.opsForValue().set("wechat:" + phoneNumber, token, 1, TimeUnit.HOURS);

        return Result.success(token);
    }

    /**
     * @Description: 获得openId
     * @author zhuliang
     * @date 2020/12/24 17:17
     */
    @Override
    public Result getOpenId(String code, String token) {
        //得到租户联系方式
        String phone = JWT.decode(token).getClaim("phone").asString();

        HashMap<String, String> map = new HashMap();

        //先去数据库查询，是否有openid
        String openId = tenantMapper.getOpenID(phone);
        if (StringUtils.isNotBlank(openId)) {

            map.put("url", null);
            map.put("openId", openId);

        } else {

            //拼接查询路径信息
            String url="/apiWxQq/sns/oauth2/access_token?appid="+basicConfiguration.getAppId()
                    + "&secret="+basicConfiguration.getAppSecret()
                    + "&code="+code
                    + "&grant_type=authorization_code";

            map.put("url", url);
            map.put("openId", null);
        }

        return Result.success(map);
    }

    /**
     * @Description: 添加用户的openId
     * @author zhuliang
     * @date 2021/2/2 11:07
     */
    @Override
    public Result addOpenId(String openId, String token) {
        //得到租户联系方式
        String phone = JWT.decode(token).getClaim("phone").asString();

        tenantMapper.updateData(null, phone, openId);

        return Result.success();
    }

    /**
     * @Description: 获取发送短信验证码的请求信息
     * @author zhuliang
     * @date 2021/2/1 14:37
     */
    private String sendVerificationCode(String verificationCode, String phone) throws Exception {
        String service = "sms";
        String host = "sms.tencentcloudapi.com";
        String region = "ap-guangzhou";
        String action = "SendSms";
        String version = "2019-07-11";
        String algorithm = "TC3-HMAC-SHA256";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区，否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1：拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";


        HashMap map = new HashMap();
        map.put("PhoneNumberSet",new String[]{"+86" + phone});
        map.put("TemplateID", basicConfiguration.getSmsTemplateID());
        map.put("SmsSdkAppid", basicConfiguration.getSmsAppid());
        map.put("Sign", basicConfiguration.getSmsSign());
        map.put("TemplateParamSet",new String[]{verificationCode});
        JSONObject jsonObject = new JSONObject(map);
        String payload = jsonObject + "";
        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;

        // ************* 步骤 2：拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;

        // ************* 步骤 3：计算签名 *************
        byte[] secretDate = hmac256(("TC3" + basicConfiguration.getSecretKey()).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();

        // ************* 步骤 4：拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + basicConfiguration.getSecretId() + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        TreeMap<String, String> headers = new TreeMap<String, String>();
        headers.put("Authorization", authorization);
        headers.put("Content-Type", CT_JSON);
        headers.put("Host", host);
        headers.put("X-TC-Action", action);
        headers.put("X-TC-Timestamp", timestamp);
        headers.put("X-TC-Version", version);
        headers.put("X-TC-Region", region);
        headers.put("X-TC-Language", "zh-CN");

        return HttpUtil.smsPost(basicConfiguration.getNginxUrlWeChat() + "/sms", jsonObject, headers);
    }

    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }

    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }
}
